/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package service

import (
	"encoding/hex"

	"hundsun.com/hsl/hschain/common/util/pb"
	"hundsun.com/hsl/hschain/contract"
	ecom "hundsun.com/hsl/hschain/executor/common"
	"hundsun.com/hsl/hschain/protos/execute"
	"hundsun.com/hsl/hschain/protos/ledger"
)

// ExecTx exec tx
func ExecTx(ctx *ecom.ExecContent, gtx *execute.GroupedTransaction) (gtxres *execute.GroupedTransaction, mid bool) {
	tx := gtx.GetTx()
	txHash := tx.Hash()
	log.Tracef("execTx txHash in exe is：%s", hex.EncodeToString(txHash))

	// 总体初始化
	receipt := &ledger.TransactionReceipt{
		TxHash:           txHash,
		BlockNumber:      ctx.GetHeight(),
		TransactionIndex: gtx.GetTxIndex(),
		TxStatus:         ledger.TransactionReceipt_SUCCESSFUL,
		Events:           nil,
		ContractId:       tx.ContractId,
		Logs:             []*ledger.Log{},
	}
	gtx.TxExecRst = &ledger.TransactionExecResult{Receipt: receipt}

	// 非创世区块才需要验证tx
	if ctx.GetHeight() > 0 {
		// checkTx
		gtxres, pass := checkTx(gtx)
		if !pass {
			return gtxres, false
		}
	}

	execName := tx.Ty
	defer func() { // 不能panic掉
		if r := recover(); r != nil {
			log.Errorf("execTx driver %s panic error %v ", execName, r)
			if gtxres == nil {
				gtxres = gtx
			}
			if gtxres.GetTxExecRst().GetReceipt() != nil &&
				gtxres.TxExecRst.Receipt.TxStatus == ledger.TransactionReceipt_SUCCESSFUL {
				gtxres.TxExecRst.Receipt.TxStatus = ledger.TransactionReceipt_FAILED_OTHER_REASON
			}
			mid = checkMiddress(gtxres)
			return
		}
	}()

	// 根据执行器类型载入相应执行器
	driver, err := contract.LoadDriver(execName, ctx.GetHeight())
	if err != nil {
		// TODO 如果没有执行器类型载入，则只做扣手续费处理
		log.Errorf("execTx load contract driver fail, exec type is %s", execName)
		gtx.TxExecRst.Receipt.TxStatus = ledger.TransactionReceipt_TX_EXEC_TYPE_UNMATCH
		return gtx, false
	}
	// 将driver做缓存
	defer contract.AddDriverHandle(execName, driver)

	// 执行器运行
	gtxres, err = driver.Run(ctx, gtx)
	if gtxres == nil {
		gtxres = gtx
	}
	if err != nil {
		if gtxres.GetTxExecRst().GetReceipt() != nil &&
			gtxres.TxExecRst.Receipt.TxStatus == ledger.TransactionReceipt_SUCCESSFUL {
			gtxres.TxExecRst.Receipt.TxStatus = ledger.TransactionReceipt_FAILED_OTHER_REASON
		}
		log.Errorf("execTx run tx fail error %v ,tx hash is：%s", err, hex.EncodeToString(txHash))
	}
	return gtxres, checkMiddress(gtxres)
}

// checkMiddress
// midAddr如果AddressRWStatus中存在数据则将midAddr置为true,
// 到regroup中判断是否需要重新分组
func checkMiddress(gtx *execute.GroupedTransaction) bool {
	midAddr := false
	if len(gtx.VarsRWStatus) >= 3 {
		midAddr = true
	} else {
		for _, addrSta := range gtx.VarsRWStatus {
			key := string(addrSta.Key)
			if key == gtx.GetTx().GetFrom() ||
				key == gtx.GetTx().GetTo() {
				continue
			}
			midAddr = true
		}
	}
	return midAddr
}

func checkTx(gtx *execute.GroupedTransaction) (*execute.GroupedTransaction, bool) {
	if gtx == nil || gtx.GetTx() == nil {
		gtx.TxExecRst.Receipt.TxStatus = ledger.TransactionReceipt_FAILED_OTHER_REASON
		return gtx, false
	}
	tx := gtx.GetTx()
	// from地址为空，非法交易
	if tx.GetFrom() == "" {
		gtx.TxExecRst.Receipt.TxStatus = ledger.TransactionReceipt_SENDER_ACCOUNT_NOT_EXIST
		return gtx, false
	}
	// fee比较
	if tx.GetFee() < pb.GetGenesisTransactionPayload().GetFeeLimit().GetMinTxFee() {
		log.Errorf("fee is small than minTxFee")
		//设置交易执行错误结果
		gtx.TxExecRst.Receipt.TxStatus = ledger.TransactionReceipt_FEE_NOT_ENOUGH
		return gtx, false
	}
	// value和fee不能为负数
	if tx.GetValue() < 0 {
		log.Errorf("value is must be positive!")
		//设置交易执行错误结果
		gtx.TxExecRst.Receipt.TxStatus = ledger.TransactionReceipt_INSUFFICIENT_FUNDS
		return gtx, false
	}
	// 签名验证
	if !checkTxSign(tx) {
		log.Errorf("tx hash[%s] signature is invalid!", hex.EncodeToString(tx.Hash()))
		//设置交易执行错误结果
		gtx.TxExecRst.Receipt.TxStatus = ledger.TransactionReceipt_SIGNATURE_INVALID
		return gtx, false
	}
	return gtx, true
}

func checkTxSign(tx *ledger.Transaction) bool {
	// TODO 当前签名只有一种，后续会加入acl
	return tx.Verify()
}
